/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

#define CORE_PRIVATE

#include "ath.h"

/* private prototypes: apache api related */
static void * _athena_conf_ap_init(apr_pool_t *p, server_rec *s);
static int    _athena_post_config(apr_pool_t *pconf, apr_pool_t *plog, 
				 apr_pool_t *ptemp, server_rec *s);
static void   _athena_child_init(apr_pool_t *p, server_rec *s);
static int    _athena_handler(request_rec *r);
#ifdef ATH_ADD_FORWARD_NETSCAPE
static int    _athena_fixups(request_rec *r);

/* private prototypes: module specifc */
static int    _athena_add_header_forward_addr(request_rec *r);
#endif
static void   _athena_shm_blob_setup(apr_pool_t *pool, server_rec *s);
static apr_status_t _athena_shm_blob_cleanup(void *unused);

/* _athena_post_config
 */
static int 
_athena_post_config(apr_pool_t *pool, apr_pool_t *plog, 
		    apr_pool_t *ptemp, server_rec *s)
{
    int i;
    ath_conf_ap *aconf = ATH_CONF_AP_GET(s);
    ath_lock *lock;
    void *data;
    const char *userdata_key = "mod_athena_init";
    ASSERT(aconf);

    /* this logic borrowed for mod_auth_digest.c */
    /* prevents this code from being called on first pass through */
    apr_pool_userdata_get(&data, userdata_key, s->process->pool);
    if (!data)
	apr_pool_userdata_set((const void *)1, userdata_key,
			      apr_pool_cleanup_null, s->process->pool);
    else
      {
	  /* here we honor reconfig status, not shm status */
	  if (!(aconf->engine_on)) 
	      return OK;

	  if (!aconf->read_only)
	    {
		ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s,
			     "%s %s [module init, mutex type: %s, shm size: %ld]", 
			     PACKAGE, VERSION, ATH_STR_LOCKMECH, ATH_SHM_SIZE);
		/* really set up */
		aconf->phys_count = ath_list_size(aconf->phys_cfgs);
		aconf->farm_count = ath_list_size(aconf->farm_cfgs);

		/* check for row level locking */
		if (aconf->mux_count == ATH_EOA) 
		    aconf->mux_count = 
		      aconf->phys_count + aconf->farm_count + 1;

		/* safe to set and create mutexes */
		for (i = 0; i < aconf->mux_count; i++)
		  {
		      lock = ath_lock_raw_for_idx(i);
		      /* set path needs an index eg path.idx */
		      lock->idx = i; 
		      ath_lock_setpath(pool, lock, aconf->mux_prefix);
		      ath_lock_create(pool, lock);
		  }

		_athena_shm_blob_setup(pool, s);
		apr_pool_cleanup_register(pool, NULL, _athena_shm_blob_cleanup,
					  apr_pool_cleanup_null);
	    }
	  else
	    {
		char *err_msg_suffix = "cannot be used in read-only mode.";
		if (ath_list_size(aconf->farm_cfgs) > 0)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthFarm %s", err_msg_suffix);
		if (aconf->engine_hit_adds_to != ATH_ALGO_NON)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthAlgoHitAdds %s", err_msg_suffix);
		if (aconf->engine_expect_update)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthExpectUpdate %s", err_msg_suffix);
		if (aconf->engine_default_phys_on)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthDefaultPhysOn %s", err_msg_suffix);
		if (aconf->engine_default_farm_on)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthDefaultFarmOn %s", err_msg_suffix);
		if (aconf->engine_algo != NULL)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthAlgorithm %s", err_msg_suffix);     
		if (aconf->engine_max_excluded)
		    ap_log_error(APLOG_MARK, APLOG_STARTUP, APR_EINVAL, s, 
				 "AthAlgoMaxExcluded %s", err_msg_suffix);
	    }
	  /* initiliaze the lookup hash caches */
	  ath_phys_cache_init(pool);
	  ath_farm_cache_init(pool);
	  ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s,
		       "smartfarm enable: %d hashdata: %s", 
		       aconf->smartfarm_enable, aconf->smartfarm_hashdata);
	  if (aconf->smartfarm_enable)
	      ath_smartfarm_init(pool, aconf->smartfarm_hashdata);
      }

    return OK;
}

static void
_athena_shm_blob_setup(apr_pool_t *pool, server_rec *s)
{
    int rv;
    int i, j;
    int result;
    int lock_idx = 0;
    ath_farm *farm;
    ath_phys *phys;
    ath_engine *engine;
    ath_cfg_farm *farm_cfg;
    ath_cfg_phys *phys_cfg;
    ath_conf_ap *aconf = ATH_CONF_AP_GET(s);

    /* only the first proc/thread to get lock should follow */
    /* "if" path, all others to "else" */
    if ((rv = ath_shm_create(pool, aconf->shm_path)) 
	== APR_SUCCESS)
      {
	  ath_node *listctx1, *listctx2;

	  /* preformat shared mem seg */
	  ath_shm_blob_format();
	  ath_engine_configure(pool, aconf);
	  /* engine lock currently unused */
	  /* only thing updated here is atomic (++ to hit counts) */
	  engine = ath_engine_get();
	  engine->lock_idx = 0; /* idx = 0 */
	  
	  /* populate shm phys array */
	  /* from what we read in from apache config */
	  i = 0;
	  ath_list_rewind_r(aconf->phys_cfgs, &listctx1);
	  while (ath_list_hasnext_r(aconf->phys_cfgs, &listctx1))
	    {
		phys_cfg = (ath_cfg_phys *) 
		  ath_list_getnext_r(aconf->phys_cfgs, &listctx1);
		phys = ath_phys_raw_for_idx(i);
		/* use up locks that we have, */
		/* wrap back to share if out */
		if (lock_idx == aconf->mux_count)
		    lock_idx = 0;
		ath_phys_configure(phys, phys_cfg, lock_idx);
		lock_idx++;
		i++;
	    }
	  /* populate shm farm array */ 
	  /* from what we read in from apache config */
	  i = 0;
	  ath_list_rewind_r(aconf->farm_cfgs, &listctx2);
	  while (ath_list_hasnext_r(aconf->farm_cfgs, &listctx2))
	    {
		farm_cfg = (ath_cfg_farm *) 
		  ath_list_getnext_r(aconf->farm_cfgs, &listctx2);
		farm = ath_farm_raw_for_idx(i);
		/* continue to use up locks, wrap back to share if out */
		if (lock_idx == aconf->mux_count)
		    lock_idx = 0;
		ath_farm_configure(farm, farm_cfg, lock_idx);
		lock_idx++;
		i++;
	    }
      }
    else
      {	  
	  ATH_APLOG(APLOG_EMERG, rv, 
		    ATH_ESHMNCRE, aconf->shm_path, ATH_SHM_SIZE);
	  /* no return needed, just exit */
	  exit(APEXIT_CHILDFATAL);
      }
}

static apr_status_t 
_athena_shm_blob_cleanup(void *unused)
{
    DEBUGLOG("cleanup shm: pid: [%d]", getpid());
    fflush(stderr);
    ath_shm_destroy();
}

static void 
_athena_child_init(apr_pool_t *pool, server_rec *s)
{
    int rv;
    int i, j;
    int result;
    ath_lock *lock;
    ath_conf_ap *aconf = ATH_CONF_AP_GET(s);

    if (!(aconf->engine_on))
	return;

    /* read only assumes previously started engine has set up
     * ath_shm_obj and locks
     */
    if (!(aconf->read_only))
      {
	  /* init all the mutexs for each child */
	  for (i = 1; i < aconf->mux_count; i++)
	    {
		lock = ath_lock_raw_for_idx(i);
		ath_lock_init(pool, lock);
	    }
	  DEBUGLOG("child shm inherit: pid: [%"APR_PID_T_FMT"] path: [%s] time: [%"APR_TIME_T_FMT"]",
		   getpid(), aconf->shm_path, apr_time_now());
	  
      }		
    else /* read only "strict" config startup */
      {
	  DEBUGLOG("read-only child: pid: [%"APR_PID_T_FMT"] path: [%s] time: [%"APR_TIME_T_FMT"]", 
		   getpid(), aconf->shm_path, apr_time_now());
      }
}

/* our content handler */
/* intercepts requests headed to engine */
/* load balances requests headed to proxy */
static int 
_athena_handler(request_rec *r)
{
    ath_conf_ap *aconf = ATH_CONF_AP_GET(r->server);
    ath_engine *engine;
    int i, j;
    int i_ath_pathlen;
    int result;
    char *prox_uri, *prox_protoport, *prox_filename;
    char *farmname, *physhost;
    ASSERT(aconf);

    /* is engine turned on: */ 
    if (!aconf->engine_on)
	return DECLINED;

    engine = ath_engine_get();  
    i_ath_pathlen = strlen(engine->path);

    /* check if request not for update/status engine */  
    /* config setup ensures trailing "/" in engine->path */
    if ((strncmp(r->uri, engine->path, i_ath_pathlen))
	/* catch path w/o trailing "/" */
	&& ((strlen(r->uri) != i_ath_pathlen - 1) 
	    || (strncmp(r->uri, engine->path, i_ath_pathlen - 1))))
      {
	  /* now check if it was reverse proxied */
	  if (r->proxyreq == PROXYREQ_REVERSE)
	      ath_request_balance_proxyreq(r);
	  else /* keep track of pass throughs */
	      engine->stats->hits_reject++;
	  /* we decline so proxy handler will take it next */
	  return DECLINED;      
      }
    /* we handle this request through to completion 
     * must be an update or status request so pass off to engine.
     */
    r->content_type = "text/plain";      
    if (!r->header_only)
      {
	  const char *str_sep1 = ATH_REQ_JOIN; 
	  const char *str_sep2 = ATH_REQ_ARG_JOIN; 
	  char *str_tok, *str_uri, *str_args;      
	  char *inter1, *inter2;
	  int i = 0;
	  int first_req_bala = 1;
	  int first_req_updt = 1;
	  int first_req_stat = 1;
	  char *str_ath_req = r->args;

	  /* 2003 10 02 mw removed no arg refusal, */
	  /* let handlers deal with it */

	  /* every query must have args (stuff after ?) */
	  /* 	  if (r->args == NULL) */
	  /* 	    { */
	  /* 		engine->stats->hits_error++; */
	  /* 		ap_rputs(ATH_EENINVAL, r); */
	  /* 		return OK; */
	  /* 	    } */

	  /* correct if missing initial "?" */
	  /* treats every request the same, embedded or not */
	  /* simply put, it skips us ahead of the engine-path */
	  /* eg.: */
	  /* /athena/update/phys?h=app1,c=1&/update/phys?h=app2,c=0 */
	  /* is the same as */
	  /* /athena/?/update/phys?h=app1,c=1&/update/phys?h=app2,c=0 */
	  if (strlen(r->uri) > i_ath_pathlen) 
	      /* i_ath_pathlen - 1 gives the "/", which is needed */
	      str_ath_req = 
		apr_pstrcat(r->pool, 
			    r->uri + i_ath_pathlen - 1, "?", 
			    r->args, 
			    NULL);
	  else
	      str_ath_req = r->args;

	  /* separates multiple engine requests in uri on '&' */
	  str_tok = apr_strtok(str_ath_req, str_sep1, &inter1);
	  /* dont allow more than max embedded requests */
	  while ((str_tok != NULL) && (i < ATH_MAX_EMBED))
	    {
		if (i > 0) /* double newline separates multiple returns */
		    ap_rputs("\n", r);
		str_uri = apr_strtok(str_tok, str_sep2, &inter2);
		str_args = apr_strtok(NULL, str_sep2, &inter2);
		/* catch escaped url arg */
		/* only needed if tool sending reqs escapes the args */
		if ((str_args)
		    && ((*str_args) == ATH_CASE_URLESC))
		  {
		      ap_unescape_url(str_args);
		      str_args++; /* step past E (ATH_CASE_URLESC */
		  }
		if ((!strncmp(str_uri, ATH_REQ_BALANCE, 
			      strlen(ATH_REQ_BALANCE)))
		    && ((aconf->diverse_multi) || (first_req_bala)))
		  {
		      r->uri = str_uri + strlen(ATH_REQ_BALANCE);
		      r->args = str_args;
		      ath_request_balance_req(r);
		      first_req_updt = 0;
		      first_req_stat = 0;
		  }
		else if ((!strncmp(str_uri, ATH_REQ_UPDATE, 
				   strlen(ATH_REQ_UPDATE)))
			 && ((aconf->diverse_multi) || (first_req_updt)))
		  {
		      r->uri = str_uri + strlen(ATH_REQ_UPDATE);
		      r->args = str_args;
		      ath_request_update(r);
		      first_req_bala = 0;
		      first_req_stat = 0;
		  }
		else if ((!strncmp(str_uri, ATH_REQ_STATUS, 
				   strlen(ATH_REQ_STATUS)))
			 && ((aconf->diverse_multi) || (first_req_stat)))
		  {
		      r->uri = str_uri + strlen(ATH_REQ_STATUS);
		      r->args = str_args;
		      ath_request_status(r);
		      first_req_bala = 0;
		      first_req_updt = 0;
		  }
		else if ((!strncmp(str_uri, ATH_REQ_MIRROR, 
				   strlen(ATH_REQ_MIRROR)))
			 && (aconf->allow_mirror)
			 && ((aconf->diverse_multi) || (first_req_stat)))
		  {
		      r->uri = str_uri + strlen(ATH_REQ_MIRROR);
		      r->args = str_args;
		      ath_request_mirror(r);
		      first_req_bala = 0;
		      first_req_updt = 0;
		  }
		else if ((!strncmp(str_uri, ATH_REQ_HELLO, 
				   strlen(ATH_REQ_HELLO)))
			 && ((aconf->diverse_multi) || (first_req_stat)))
		  {
		      r->uri = str_uri + strlen(ATH_REQ_HELLO);
		      r->args = str_args;
		      ath_request_hello(r);
		      first_req_bala = 0;
		      first_req_updt = 0;
		  }
		else
		  {
		      engine->stats->hits_error++;
		      /* return error */
		      ap_rputs(ATH_EENINVAL, r);
		      /* log bad reg */
		      ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
				   ATH_EENBADRE, r->uri, r->args);
		  }
		i++;
		str_tok = apr_strtok(NULL, str_sep1, &inter1);
	    }
	  /* on way out, check if we hit limit */
	  if (i >= ATH_MAX_EMBED)
	      ap_log_error(APLOG_MARK, APLOG_WARNING, 0, NULL, 
			   ATH_EEMBOVER, ATH_MAX_EMBED);
      }
    return OK;
}


/* the following two funcs add the requesting client's ip as a new
 * header in the request, since remote_ip gets overridden by proxy
 * undef ATH_ADD_FORWARD in makefile if you do not want it at all
 *
 * it mimics the Netscape proxy's insertion of the non-standard
 * header "client-ip"
 * this is named something else in most other proxies:
 * header "X-Forwarded-For"
 * undef ATH_ADD_FORWARD_NETSCAPE to use the latter
 * note: some chose to append to not clobber existing
 * like-named headers.
 * i clobber.
 */
#ifdef ATH_ADD_FORWARD_NETSCAPE
static int 
_athena_fixups(request_rec *r)
{
    _athena_add_header_forward_addr(r);
 return DECLINED;
}

static int 
_athena_add_header_forward_addr(request_rec *r)
{
    /* set that stupid client-ip header */
    if (r->proxyreq) 
	apr_table_set(r->headers_in, "client-ip", r->connection->remote_ip);
    /* #else */
    /* apache now takes care of this already */
    /*   if (r->proxyreq)  */
    /*   apr_table_set(r->headers_in, "X-Forwarded-For", r->connection->remote_ip); */
}
#endif

static void * 
_athena_conf_ap_init(apr_pool_t *p, server_rec *s)
{
    ath_conf_ap *aconf = ath_conf_ap_alloc(p);
    /* default settings (as opposed to formatted settings in alloc) */
    aconf->shm_path = ATH_SHM_PATH;
    aconf->mux_prefix = ATH_MUX_PREFIX;
    aconf->mux_count = ATH_EOA;
    aconf->diverse_multi = ATH_ON;
    aconf->allow_mirror = ATH_OFF;
    aconf->read_only = ATH_READ_WRITE;
    aconf->engine_on = ATH_ENGINE_ON;
    aconf->engine_default_phys_on = ATH_DEFAULT_STATUS;
    aconf->engine_default_farm_on = ATH_DEFAULT_STATUS;
    aconf->engine_expect_update = ATH_OFF;
    aconf->engine_allow_ttl = ATH_TTL;
    aconf->engine_algo = ATH_ALGO_STR_DEFAULT;
    aconf->engine_path = ATH_ENGINE_PATH;
    aconf->engine_nominate_per_update = ATH_OFF; /* default is per proxy req */
    aconf->engine_hit_adds_to = ATH_ALGO_NON;
    aconf->engine_hit_adds = 0;
    aconf->engine_status_farmfmt = ATH_STAT_FARM_FMT;
    aconf->engine_status_physfmt = ATH_STAT_PHYS_FMT;
    aconf->engine_status_engifmt = ATH_STAT_ENGI_FMT;
    aconf->cookie_name_smartfarm = ATH_COOKIE_NAME_SMARTFARM_DEFAULT;
    aconf->smartfarm_enable = ATH_OFF;
    aconf->smartfarm_hashdata = NULL;
    return aconf;
}


static const command_rec _athena_conf_cmds[] =
    {
	AP_INIT_FLAG("AthEngine", ath_conf_engine, NULL, RSRC_CONF,
		     "set to On to enable engine request handler"),
	AP_INIT_FLAG("AthFarmNameForceCase", ath_conf_farmname_forcecase, NULL, RSRC_CONF,
		     "set to Off disable forced lower case farm names, will break ProxyPass"),
	AP_INIT_FLAG("AthDefaultPhysOn", ath_conf_default_phys_on, NULL, RSRC_CONF,
		     "set to On to make physicals default to online"),
	AP_INIT_FLAG("AthDefaultFarmOn", ath_conf_default_farm_on, NULL, RSRC_CONF,
		     "set to On to make farms default to online"),
	AP_INIT_FLAG("AthAllowMirror", ath_conf_mirror, NULL, RSRC_CONF,
		     "set to On to allow hits to mirror"),
	AP_INIT_FLAG("AthAllowDiverseMulti", ath_conf_diverse_multi, NULL, RSRC_CONF,
		     "set to On to allow different types in embedded requests"),
	AP_INIT_FLAG("AthExpectUpdate", ath_conf_expectation, NULL, RSRC_CONF,
		     "set to On to make phys ttls active "),
	AP_INIT_FLAG("AthNominationPerUpdate", ath_conf_nomination, NULL, RSRC_CONF,
		     "set to On to rebalance after update NOT during proxy request"),
	AP_INIT_FLAG("AthAlgoMaxExcluded", ath_conf_max_excluded, NULL, RSRC_CONF,
		     "set to On to exclude a dynamic member who has a max in any field"),
	AP_INIT_FLAG("AthAllDownURLForceGet", ath_conf_downurl_forceget, NULL, RSRC_CONF,
		     "set to On to change all proxy reqs to GET if all down"),
	AP_INIT_FLAG("AthOfflineURLForceGet", ath_conf_offlineurl_forceget, NULL, RSRC_CONF,
		     "set to On to change all proxy reqs to GET if all offline"),
	AP_INIT_FLAG("AthSmartfarmEnable", ath_conf_smartfarm_enable, NULL, RSRC_CONF,
		     "Allow farm name manipulation by cookie"),
	AP_INIT_TAKE1("AthPath", ath_conf_path, NULL, RSRC_CONF,
		      "URL dir path to access athena engine, e.g. /athena"),
	AP_INIT_TAKE1("AthAllDownURL", ath_conf_downurl, NULL, RSRC_CONF,
		      "URL to use when all physicals in a farm are down"),
	AP_INIT_TAKE1("AthOfflineURL", ath_conf_offlineurl, NULL, RSRC_CONF,
		      "URL to use when all physicals in a farm are offline"),
	AP_INIT_TAKE1("AthSHMFile", ath_conf_shmpath, NULL, RSRC_CONF,
		      "path to shared memory, e.g. logs/ath.shm"),
	AP_INIT_TAKE1("AthMUXPrefix", ath_conf_muxpath, NULL, RSRC_CONF,
		      "path to prefix mutexes, e.g. logs/ath.mux."),
	AP_INIT_TAKE1("AthMUXCount", ath_conf_muxcount, NULL, RSRC_CONF,
		      "Number of mutexes to share, or word \"row-level\" for optimal"),
	AP_INIT_TAKE1("AthExpectUpdateField", ath_conf_expects_field, NULL, RSRC_CONF,
		      "set to [cpu|net|ld|mem|1cus|2cus|3cus] to force AthExpectUpdate on a specfic field, scope is engine not farm"),
	AP_INIT_TAKE1("AthReadOnlyPolicy", ath_conf_readonly, NULL, RSRC_CONF,
		      "policy for read only access: none | strict | hit-adds"),
	AP_INIT_TAKE1("AthDefaultPhysStatusFormat", ath_conf_physfmt, NULL, RSRC_CONF,
		      "default output format for physical status request"),
	AP_INIT_TAKE1("AthDefaultFarmStatusFormat", ath_conf_farmfmt, NULL, RSRC_CONF,
		      "default output format for farm status request"),
	AP_INIT_TAKE1("AthDefaultEngineStatusFormat", ath_conf_engifmt, NULL, RSRC_CONF,
		      "default output format for engine status request"),
	AP_INIT_TAKE1("AthExpectTTL", ath_conf_ttl, NULL, RSRC_CONF,
		      "seconds without update before physical is marked down"),
	AP_INIT_TAKE1("AthExpectRecoverTTL", ath_conf_recover_ttl, NULL, RSRC_CONF,
		      "seconds before physical is marked up after new updates begin"),
	AP_INIT_TAKE1("AthAlgorithm", ath_conf_algorithm, NULL, RSRC_CONF,
		      "indicates algorithm configuration"),
	AP_INIT_TAKE1("AthCookieNameSmartfarm", ath_conf_cookie_name_smartfarm, NULL, RSRC_CONF,
		      "the name of the cookie containing Smartfarm settings"),
	AP_INIT_TAKE1("AthSmartfarmProtect", ath_conf_smartfarm_protect, NULL, RSRC_CONF,
		      "set secret data and enable securing of the smartfarm cookie with hash"),
	AP_INIT_TAKE1("AthSmartfarmTamperUrl", ath_conf_smartfarm_tamper_url, NULL, RSRC_CONF,
		      "change proxy destination here if cookie is tampered with"),
	AP_INIT_TAKE2("AthAlgoHitAdds", ath_conf_hitadds, NULL, RSRC_CONF,
		      "first value is desired field, second is addend"),
	AP_INIT_RAW_ARGS("<AthFarm", ath_conf_farm, NULL, RSRC_CONF,
			 "Contains farm name, nested inside are parameters and members"),
	/* Directives inside <AthFarm ...> are handled 
	 * in ath_conf_farm method
	 */
	{NULL}
    };  

static void register_hooks(apr_pool_t *p)
{
    static const char * const aszPre[]={ "mod_proxy.c", NULL };

    /* post config shm memory setup */
    ap_hook_post_config(_athena_post_config, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_child_init(_athena_child_init, NULL, NULL, APR_HOOK_MIDDLE);
    /* everything we do we do in handler except fixup (add header)*/
    ap_hook_handler(_athena_handler, NULL, aszPre, APR_HOOK_FIRST);  
#ifdef ATH_ADD_FORWARD_NETSCAPE
    ap_hook_fixups(_athena_fixups, NULL, NULL, APR_HOOK_LAST);
#endif
    /* ap_hook_log_transaction(_athena_log_transaction,NULL,NULL,APR_HOOK_FIRST); */
}


/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA athena_module = 
    {
	STANDARD20_MODULE_STUFF, 
	NULL,                  /* create per-dir    config structures */
	NULL,                  /* merge  per-dir    config structures */
	_athena_conf_ap_init,   /* create per-server config structures */
	NULL,                  /* merge  per-server config structures */
	_athena_conf_cmds,      /* table of config file commands       */
	register_hooks         /* register hooks                      */
    };

